﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NetworkManagement;
using DataObject = LorraineSec.DatabaseLib.DataObjects;

namespace LorraineSec.MessageLib.Messages
{
    public sealed class LogListResponse : EncryptedMessage
    {
        #region Format Constants
        /*
         * Format:
         *        2           4            4          4         4        8           1               1            x            y
         * | # LogLists | { LogID | CompartmentID | UserID | AssetID | Time | DeviceSerialLen | ContentLen | DeviceSerial | Content }
         * 
         */

        //lengths
        private const int ENC_NUMLOGLISTS_LENGTH = 2;
        private const int SET_LOGID_LENGTH = 4;
        private const int SET_COMPARTMENTID_LENGTH = 4;
        private const int SET_USERID_LENGTH = 4;
        private const int SET_ASSETID_LENGTH = 4;
        private const int SET_TIME_LENGTH = 8;
        private const int SET_DEVSERIALLEN_LENGTH = 1;
        private const int SET_CONTENTLEN_LENGTH = 1;

        //offsets
        private const int ENC_NUMLOGLISTS_OFFSET = 0;
        private const int SET_LOGID_OFFSET = 0;
        private const int SET_COMPARTMENTID_OFFSET = SET_LOGID_OFFSET + SET_LOGID_LENGTH;
        private const int SET_USERID_OFFSET = SET_COMPARTMENTID_OFFSET + SET_COMPARTMENTID_LENGTH;
        private const int SET_ASSETID_OFFSET = SET_USERID_OFFSET + SET_USERID_LENGTH;
        private const int SET_TIME_OFFSET = SET_ASSETID_OFFSET + SET_ASSETID_LENGTH;
        private const int SET_DEVSERIALLEN_OFFSET = SET_TIME_OFFSET + SET_TIME_LENGTH;
        private const int SET_CONTENTLEN_OFFSET = SET_DEVSERIALLEN_OFFSET + SET_DEVSERIALLEN_LENGTH;
        private const int SET_DEVSERIAL_OFFSET = SET_CONTENTLEN_OFFSET + SET_CONTENTLEN_LENGTH;

        //known lengths
        private const int ENC_KNOWN_LENGTH = ENC_NUMLOGLISTS_LENGTH;
        private const int SET_KNOWN_LENGTH = SET_LOGID_LENGTH + SET_COMPARTMENTID_LENGTH + SET_USERID_LENGTH + SET_ASSETID_LENGTH + SET_TIME_LENGTH + SET_DEVSERIALLEN_LENGTH + SET_CONTENTLEN_LENGTH;

        #endregion

        public struct Event
        {
            public Event(uint _id, long _time, uint _compID, uint _userID, uint _assetID, string _dev, string _content)
            {
                ID = _id;
                Time = _time;
                CompartmentID = _compID;
                UserID = _userID;
                AssetID = _assetID;
                DeviceSerial = _dev;
                Content = _content;
            }
            public static implicit operator Event(DataObject.Event obj)
            {
                return new Event(obj.ID, obj.Timestamp, obj.CompartmentID, obj.UserID, obj.AssetID, obj.DeviceSerial, obj.Content);
            }

            public uint ID;
            public long Time;
            public uint CompartmentID;
            public uint UserID;
            public uint AssetID;
            public string DeviceSerial;
            public string Content;
        }

        public List<Event> Events;

        public LogListResponse()
        {
            init();
        }

        public LogListResponse(byte[] bytes)
            : base(bytes)
        {
            init();
            decode();
        }

        private void init()
        {
            MessageID = (ushort)LorraineSecMessageFactory.MessageID.LOG_LIST_RESPONSE;
            Events = new List<Event>();
        }

        public override void decrypt(CryptoManager crypt)
        {
            base.decrypt(crypt);
            //Decode byte array
            //Get number of Log Lists
            ushort numLogLists = Bytes.getUInt16(encData, ENC_NUMLOGLISTS_OFFSET);
            //Set offset variable
            int offset = ENC_NUMLOGLISTS_OFFSET + ENC_NUMLOGLISTS_LENGTH;
            for (int i = 0; i < numLogLists; ++i)
            {
                //Get Log List ID
                uint id = Bytes.getUInt32(encData, offset);
                offset += SET_LOGID_LENGTH;
                //Get Compartment ID
                uint compartmentID = Bytes.getUInt32(encData, offset);
                offset += SET_COMPARTMENTID_LENGTH;
                //Get User ID
                uint userID = Bytes.getUInt32(encData, offset);
                offset += SET_USERID_LENGTH;
                //Get Asset ID
                uint assetID = Bytes.getUInt32(encData, offset);
                offset += SET_ASSETID_LENGTH;
                //Get Log Time
                long time = Bytes.getInt64(encData, offset);
                offset += SET_TIME_LENGTH;
                //Get length of Device Serial
                int deviceSerialLen = Bytes.getByte(encData, offset);
                offset += SET_DEVSERIALLEN_LENGTH;
                //Get length of Content
                int contentLen = Bytes.getByte(encData, offset);
                offset += SET_CONTENTLEN_LENGTH;
                //Get Log Device Serial
                string deviceSerial = Bytes.getString(encData, offset, deviceSerialLen);
                offset += deviceSerialLen;
                //Get Log Content
                string content = Bytes.getString(encData, offset, contentLen);
                offset += contentLen;
                //Add Log to dictionary
                Events.Add(new Event(id, time, compartmentID, userID, assetID, deviceSerial, content));
            }
        }

        public override void encrypt(CryptoManager crypt)
        {
            //Encode byte array
            //Precount byte array allocation size
            int strAllocSize = 0;
            foreach (Event str in Events)
            {
                strAllocSize += str.DeviceSerial.Length;
                strAllocSize += str.Content.Length;
            }

            //Allocate memory for encrypion field
            encData = new byte[ENC_KNOWN_LENGTH + Events.Count * SET_KNOWN_LENGTH + strAllocSize];

            //Set the number of Log Lists
            Bytes.setValue(encData, ENC_NUMLOGLISTS_OFFSET, (ushort)Events.Count);
            //Set offset variable
            int offset = ENC_NUMLOGLISTS_OFFSET + ENC_NUMLOGLISTS_LENGTH;

            foreach (Event eve in Events)
            {
                //Set Log ID
                Bytes.setValue(encData, offset, eve.ID);
                offset += SET_LOGID_LENGTH;
                //Set Compartment ID
                Bytes.setValue(encData, offset, eve.CompartmentID);
                offset += SET_COMPARTMENTID_LENGTH;
                //Set User ID
                Bytes.setValue(encData, offset, eve.UserID);
                offset += SET_USERID_LENGTH;
                //Set Asset ID
                Bytes.setValue(encData, offset, eve.AssetID);
                offset += SET_ASSETID_LENGTH;
                //Set Log Time
                Bytes.setValue(encData, offset, eve.Time);
                offset += SET_TIME_LENGTH;
                //Set Device Serial Length
                Bytes.setValue(encData, offset, (byte)eve.DeviceSerial.Length);
                offset += SET_DEVSERIALLEN_LENGTH;
                //Set Content Length
                Bytes.setValue(encData, offset, (byte)eve.Content.Length);
                offset += SET_CONTENTLEN_LENGTH;                
                //Set Log Device Serial
                Bytes.setValue(encData, offset, eve.DeviceSerial);
                offset += eve.DeviceSerial.Length;
                //Set Log Content
                Bytes.setValue(encData, offset, eve.Content);
                offset += eve.Content.Length;
            }

            base.encrypt(crypt);
        }
    }
}
